#ifndef db_RECORDBASE_H
#define db_RECORDBASE_H

#include <vector>
#include <boost/smart_ptr.hpp>
#include "Adapter.h"
#include "AdapterPtr.h"
#include "Any.h"
#include "RecordException.h"
#include "RecordErrorSet.h"
#include "Pagination.h"
#include "RecordDef.h"

#include <iostream>

namespace serika
{
	namespace db
	{
		class RecordBase
		{
		public:
			RecordBase();
			RecordBase(FieldMap* fields, bool readOnly);
			RecordBase(const RecordBase& other);
			virtual ~RecordBase();
	
			RecordBase& operator = (const RecordBase& other);
	
			static bool Initialize(const std::string& adapterName, const Any& initInfo);
	
			static void Finalize();
	
			template<typename TYPE>
			static bool EstablishConnection(
				int concurrency,
				const std::string& adapterName, 
				const std::string& database,
				const std::string& username,
				const std::string& password,
				const std::string& host,
				int port = 3306,
				int timeout = 5000,
				const std::string& encoding = "utf8");
			
			template<typename TYPE>
			static void CloseConnection();
	
			template<typename TYPE>
			static bool SetRecordAdapter(size_t adapterIndex);
	
			// Value Functions
			void SetValue(const std::string& name, const Any& value);
			const Any& GetValue(const std::string& name) const;
			template<typename TYPE>
			const TYPE& GetRawValue(const std::string& name) const;
			bool IsValueNil(const std::string& name) const;
			const FieldMap& GetFields() const { return *mFields; }
			void Assign(FieldMap* fields);
	
			FieldMap* CloneFields() const;
			
			const RecordErrorSet& GetErrorSet() const { return mErrors; }
			bool IsReadOnly() const { return mReadOnly; }
	
			virtual const char* GetTableNameI() const { return 0; }
			virtual const char* GetPrimaryKeyI() const { return 0; }
			virtual Adapter* GetAdapterI() const { return 0 ;}
	
			// CREATE functions
			template<typename TYPE>
			static TYPE* Create(FieldMap* fieldMap, bool throwOnError = false);
	
			template<typename TYPE>
			static bool Create(std::vector<TYPE*>& results, std::vector<FieldMap*>& fieldMaps, bool throwOnError = false);
	
			bool Save(bool throwOnError = false);
	
			// SELECT functions
			template<typename TYPE>
			static TYPE* Find(int id);
	
			template<typename TYPE>
			static TYPE* Find(const Any& value, const std::string& column = "");
	
			template<typename TYPE>
			static int Find(std::vector<TYPE*>& resultSet, const std::vector<int>& ids);
	
			template<typename TYPE>
			static int Find(std::vector<TYPE*>& resultSet, const std::vector<Any>& values, const std::string& column = "");
	
			template<typename TYPE>
			static TYPE* FindFirst(
				const std::string& conditions = "",
				const std::string& select = "*",
				const std::string& joins = "",
				const std::string& order = "",
				const std::string& group = "",
				const std::string& having = "");
	
			template<typename TYPE>
			static int FindAll(
				std::vector<TYPE*>& resultSet,
				const std::string& conditions = "",
				int offset = 0,
				int limit = 0,
				const std::string& select = "*",
				const std::string& joins = "",
				const std::string& order = "",
				const std::string& group = "",
				const std::string& having = "");
	
			template<typename TYPE>
			static int Count(
					const std::string& column = "*",
					const std::string& conditions = "",
					const std::string& joins = ""
				);
	
			template<typename TYPE>
			static Pagination<TYPE>* Paginate(
				int page,
				int perPage,
				const std::string& conditions = "",
				const std::string& select = "*",
				const std::string& joins = "",
				const std::string& order = "");
	
			// UPDATE Functions
			bool UpdateAttribute(const std::string& name, const Any& value);
			bool UpdateAttributes(const FieldMap* fields);
	
			template<typename TYPE>
			static int UpdateAll(const std::string& settings, const std::string& conditions);
	
			// DELETE Functions
			void Destroy();
	
			template<typename TYPE>
			static bool Delete(int id);
	
			template<typename TYPE>
			static int Delete(const Any& value, const std::string& column = "");
	
			template<typename TYPE>
			static int Delete(const std::vector<Any>& values, const std::string& column = "");
	
			template<typename TYPE>
			static int DeleteAll(const std::string& conditions = "");
	
			template<typename TYPE>
			static int Destroy(const Any& value, const std::string& column = "");
	
			template<typename TYPE>
			static int Destroy(const std::vector<Any>& values, const std::string& column = "");
	
			template<typename TYPE>
			static int DestroyAll(const std::string& conditions = "");
	
			// Marshall and Unmarshall
			ut::String ToString() const;
			bool ParseString(const ut::String& s);
		protected:
			static Adapter* mDefaultAdapter;
			static std::vector<Adapter*> mAdapters;
			static Adapter* mNullAdapter;
			FieldMap* mFields;
			RecordErrorSet mErrors;
			bool mReadOnly;
			bool mModified;
	
			// Template methods
			void DoValidate();
	
			// Hooks
			virtual void Validate() {}
			virtual void BeforeValidation() {}
			virtual void AfterValidation() {}
			virtual void BeforeSave() {}
			virtual void AfterSave() {}
			virtual void BeforeCreate() {}
			virtual void AfterCreate() {}
			virtual void BeforeUpdate() {}
			virtual void AfterUpdate() {}
			virtual void BeforeDestroy() {}
			virtual void AfterDestroy() {}
		};
	
	
		// Transaction
		////////////////////////////////////////////////////////////////////
	
		template<typename RT>
		class Transaction
		{
		public:
			Transaction(bool autoBegin = true)
				: mBegin(false)
			{
				if (autoBegin)
				{
					this->Begin();
				}
			}
	
			~Transaction()
			{
				if (mBegin)
				{
					RT::mAdapter->RollbackTransaction();
					mBegin = false;
				}
			}
	
			void Commit()
			{
				if (mBegin)
				{
					RT::mAdapter->CommitTransaction();
					mBegin = false;
				}
			}
			
			void Begin()
			{
				if (!mBegin)
				{
					RT::mAdapter->BeginTransaction();
					mBegin = true;
				}
			}
		private:
			bool mBegin;
		};
	}
	
	#include "RecordBase.inl"
	
	// Serialization
	////////////////////////////////////////////////////////////////////////
	ut::Ostream& operator << (ut::Ostream& s, const db::RecordBase& rb);
	ut::Istream& operator >> (ut::Istream& s, db::RecordBase& rb);
	
	ut::Ostream& operator << (ut::Ostream& s, const db::RecordBase* rb);
	ut::Istream& operator >> (ut::Istream& s, db::RecordBase* rb);
	
	ut::Ostream& operator << (ut::Ostream& s, const db::FieldMap& fields);
	ut::Istream& operator >> (ut::Istream& s, db::FieldMap& fields);
	
	ut::Ostream& operator << (ut::Ostream& s, const db::FieldMap* fields);
	ut::Istream& operator >> (ut::Istream& s, db::FieldMap* fields);
}


#endif
